﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace BenevolentSunData
{
    public abstract class Cell : ICloneable
    {

        #region Game Data
        public abstract String Name
        {
            get;
        }

        public virtual String Note
        {
            get { return ""; }
        }
        #endregion


        #region Map
        [ContentSerializerIgnore]
        public Map Map;

        public Point Coords;
        #endregion


        #region World
        //[ContentSerializerIgnore]
        //public Vector3 Position = Vector3.Zero;

        [ContentSerializerIgnore]
        public float Rotation = 0f;
        #endregion


        #region Initialization
        public Cell(){}

        public virtual void LoadContent(ContentManager Content, Map map) {
            Map = map;
            floorModel = Content.Load<Model>("Models/Floor");
            floorBoneTransforms = new Matrix[floorModel.Bones.Count];
            floorModel.CopyAbsoluteBoneTransformsTo(floorBoneTransforms);
            floorMaterial = map.Skin.GetMaterial("Floor");
        }
        #endregion


        #region Beams
        private Beam northBeam;
        private Beam southBeam;
        private Beam eastBeam;
        private Beam westBeam;

        public Beam GetBeam(Direction direction)
        {
            switch (direction)
            {
                case Direction.North:
                    return northBeam;
                case Direction.South:
                    return southBeam;
                case Direction.East:
                    return eastBeam;
                case Direction.West:
                    return westBeam;
                default:
                    throw (new Exception("Unknown direction: " + direction));
            }
        }

        private void ClearBeam(Direction direction)
        {
            if (GetBeam(direction) == null)
                throw (new Exception("Cannot clear beam in that direction, it has not been set! " + direction));
            switch (direction)
            {
                case Direction.North:
                    northBeam = null; break;
                case Direction.South:
                    southBeam = null; break;
                case Direction.East:
                    eastBeam = null; break;
                case Direction.West:
                    westBeam = null; break;
                default:
                    throw (new Exception("Unknown direction: " + direction));
            }
        }

        private void SetBeam(Direction direction, Beam beam)
        {
            if (GetBeam(direction) != null)
                throw (new Exception("Cannot set beam in that direction, it has not been cleared! " + direction));
            switch (direction)
            {
                case Direction.North:
                    northBeam = beam; break;
                case Direction.South:
                    southBeam = beam; break;
                case Direction.East:
                    eastBeam = beam; break;
                case Direction.West:
                    westBeam = beam; break;
                default:
                    throw (new Exception("Unknown direction: " + direction));
            }
        }

        protected bool MergeBeam(Beam beam, Direction direction)
        {
            switch (direction)
            {
                case Direction.North:
                    if (northBeam == null)
                    {
                        northBeam = beam.Clone() as Beam;
                        return true;
                    }
                    return northBeam.Merge(beam);
                case Direction.South:
                    if (southBeam == null)
                    {
                        southBeam = beam.Clone() as Beam;
                        return true;
                    }
                    return southBeam.Merge(beam);
                case Direction.East:
                    if (eastBeam == null)
                    {
                        eastBeam = beam.Clone() as Beam;
                        return true;
                    }
                    return eastBeam.Merge(beam);
                case Direction.West:
                    if (westBeam == null)
                    {
                        westBeam = beam.Clone() as Beam;
                        return true;
                    }
                    return westBeam.Merge(beam);
                default:
                    throw (new Exception("Unknown direction: " + direction));
            }
        }

        public virtual List<Direction> AddInput(Beam beam, Direction direction)
        {
            return new List<Direction>();
        }

        public virtual bool Visited(Beam beam, Direction direction)
        {
            Beam existing = GetBeam(direction);
            if (existing != null && existing.Equals(beam))
                return true;
            return false;
        }

        public virtual void Clear()
        {
            northBeam = southBeam = eastBeam = westBeam = null;
        }
        #endregion


        #region Interactions
        public virtual bool Interact()
        {
            return false;
        }
        #endregion


        #region Flooring
        protected Model floorModel;

        protected Material floorMaterial;

        protected Matrix[] floorBoneTransforms;

        public void DrawFloor(Vector3 position)
        {
            foreach (ModelMesh mesh in floorModel.Meshes)
            {
                Matrix world =
                    floorBoneTransforms[mesh.ParentBone.Index] *
                    Matrix.CreateTranslation(position);
                floorMaterial.DrawMesh(mesh, world, Map.PointLights, Map.BeamLights);
            }
        }
        #endregion


        #region Drawing
        public virtual void Draw(Vector3 position) {}
        #endregion


        #region ICloneable
        public virtual Cell Clone(Cell cell)
        {
            cell.Coords = new Point(Coords.X, Coords.Y);
            cell.floorBoneTransforms = floorBoneTransforms;
            cell.floorModel = floorModel;
            cell.floorMaterial = floorMaterial.Clone();
            return cell;
        }

        public abstract object Clone();
        #endregion

    }
}
